1/* 2 * Copyright (c) 2012, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24// 25 26import java.io.PrintStream; 27import java.util.ArrayList; 28import java.util.Arrays; 29import java.util.HashSet; 30import java.util.Iterator; 31import java.util.List; 32import java.util.Set; 33import java.util.StringTokenizer; 34import javax.imageio.metadata.IIOMetadata; 35import javax.imageio.metadata.IIOMetadataFormat; 36import javax.imageio.metadata.IIOMetadataFormatImpl; 37import javax.imageio.spi.IIORegistry; 38import javax.imageio.spi.ImageReaderSpi; 39import com.sun.imageio.plugins.png.PNGMetadata; 40 41public class MetadataFormatPrinter { 42 43 private int indentLevel = 0; 44 45 private int column = 0; 46 47 private PrintStream out; 48 49 private static final int maxColumn = 75; 50 51 private static String[] dataTypeNames = { 52 "String", "Boolean", "Integer", "Float", "Double" 53 }; 54 55 // "Infinite" values 56 private static String maxInteger = Integer.toString(Integer.MAX_VALUE); 57 58 public MetadataFormatPrinter(PrintStream out) { 59 this.out = out; 60 } 61 62 private void println() { 63 out.println(); 64 column = 0; 65 } 66 67 private void println(String s) { 68 out.println(s); 69 column = 0; 70 } 71 72 private void printWrapped(String in, int leftIndent) { 73 StringTokenizer t = new StringTokenizer(in); 74 while (t.hasMoreTokens()) { 75 String s = t.nextToken(); 76 int length = s.length(); 77 if (column + length > maxColumn) { 78 println(); 79 indent(); 80 for (int i = 0; i < leftIndent; i++) { 81 print(" "); 82 } 83 } 84 out.print(s); 85 out.print(" "); 86 column += length + 1; 87 } 88 } 89 90 private void print(String s) { 91 int length = s.length(); 92 if (column + length > maxColumn) { 93 println(); 94 indent(); 95 print(" "); 96 } 97 out.print(s); 98 column += length; 99 } 100 101 private void print(IIOMetadataFormat format) { 102 String rootName = format.getRootName(); 103 println("<!DOCTYPE \"" + 104 rootName + 105 "\" ["); 106 ++indentLevel; 107 print(format, rootName); 108 --indentLevel; 109 print("]>"); 110 println(); 111 println(); 112 } 113 114 private void indent() { 115 for (int i = 0; i < indentLevel; i++) { 116 out.print(" "); 117 column += 2; 118 } 119 } 120 121 private void printElementInfo(IIOMetadataFormat format, 122 String elementName) { 123 println(); 124 indent(); 125 print("<!ELEMENT \"" + 126 elementName + 127 "\""); 128 129 String[] childNames = format.getChildNames(elementName); 130 boolean hasChildren = true; 131 String separator = " "; // symbol to place between children 132 String terminator = ""; // symbol to follow last child 133 String repeater = ""; // "*" if repeating 134 135 switch (format.getChildPolicy(elementName)) { 136 case IIOMetadataFormat.CHILD_POLICY_EMPTY: 137 hasChildren = false; 138 break; 139 case IIOMetadataFormat.CHILD_POLICY_ALL: 140 separator = ", "; 141 break; 142 case IIOMetadataFormat.CHILD_POLICY_SOME: 143 separator = "?, "; 144 terminator = "?"; 145 break; 146 case IIOMetadataFormat.CHILD_POLICY_CHOICE: 147 separator = " | "; 148 break; 149 case IIOMetadataFormat.CHILD_POLICY_SEQUENCE: 150 separator = " | "; 151 repeater = "*"; 152 break; 153 case IIOMetadataFormat.CHILD_POLICY_REPEAT: 154 repeater = "*"; 155 break; 156 default: 157 break; 158 } 159 160 if (hasChildren) { 161 print(" ("); 162 for (int i = 0; i < childNames.length - 1; i++) { 163 print(childNames[i] + separator); 164 } 165 print(childNames[childNames.length - 1] + terminator); 166 print(")" + repeater + ">"); 167 } else { 168 print(" EMPTY>"); 169 } 170 println(); 171 172 String description = format.getElementDescription(elementName, null); 173 if (description != null) { 174 ++indentLevel; 175 indent(); 176 printWrapped("<!-- " + description + " -->", 5); 177 println(); 178 --indentLevel; 179 } 180 if (format.getChildPolicy(elementName) == 181 IIOMetadataFormat.CHILD_POLICY_REPEAT) { 182 int minChildren = format.getElementMinChildren(elementName); 183 if (minChildren != 0) { 184 indent(); 185 println(" <!-- Min children: " + 186 minChildren + 187 " -->"); 188 } 189 int maxChildren = format.getElementMaxChildren(elementName); 190 if (maxChildren != Integer.MAX_VALUE) { 191 indent(); 192 println(" <!-- Max children: " + 193 maxChildren + 194 " -->"); 195 } 196 } 197 } 198 199 private void printAttributeInfo(IIOMetadataFormat format, 200 String elementName, 201 String attrName) { 202 indent(); 203 print("<!ATTLIST \"" + 204 elementName + 205 "\" \"" + 206 attrName + 207 "\""); 208 209 int attrValueType = 210 format.getAttributeValueType(elementName, attrName); 211 switch (attrValueType) { 212 case IIOMetadataFormat.VALUE_NONE: 213 throw new RuntimeException 214 ("Encountered VALUE_NONE for an attribute!"); 215 // break; 216 case IIOMetadataFormat.VALUE_ARBITRARY: 217 print(" #CDATA"); 218 break; 219 case IIOMetadataFormat.VALUE_RANGE: 220 case IIOMetadataFormat.VALUE_RANGE_MIN_INCLUSIVE: 221 case IIOMetadataFormat.VALUE_RANGE_MAX_INCLUSIVE: 222 case IIOMetadataFormat.VALUE_RANGE_MIN_MAX_INCLUSIVE: 223 print(" #CDATA"); 224 break; 225 case IIOMetadataFormat.VALUE_ENUMERATION: 226 print(" ("); 227 String[] attrValues = 228 format.getAttributeEnumerations(elementName, attrName); 229 for (int j = 0; j < attrValues.length - 1; j++) { 230 print("\"" + attrValues[j] + "\" | "); 231 } 232 print("\"" + attrValues[attrValues.length - 1] + "\")"); 233 break; 234 case IIOMetadataFormat.VALUE_LIST: 235 print(" #CDATA"); 236 break; 237 default: 238 throw new RuntimeException 239 ("Encountered unknown value type for an attribute!"); 240 // break; 241 } 242 243 String defaultValue = 244 format.getAttributeDefaultValue(elementName, attrName); 245 if (defaultValue != null) { 246 print(" "); 247 print("\"" + defaultValue + "\""); 248 } else { 249 if (format.isAttributeRequired(elementName, attrName)) { 250 print(" #REQUIRED"); 251 } else { 252 print(" #IMPLIED"); 253 } 254 } 255 println(">"); 256 257 String description = format.getAttributeDescription(elementName, 258 attrName, 259 null); 260 if (description != null) { 261 ++indentLevel; 262 indent(); 263 printWrapped("<!-- " + description + " -->", 5); 264 println(); 265 --indentLevel; 266 } 267 268 int dataType = format.getAttributeDataType(elementName, attrName); 269 270 switch (attrValueType) { 271 case IIOMetadataFormat.VALUE_ARBITRARY: 272 indent(); 273 println(" <!-- Data type: " + dataTypeNames[dataType] + " -->"); 274 break; 275 276 case IIOMetadataFormat.VALUE_RANGE: 277 case IIOMetadataFormat.VALUE_RANGE_MIN_INCLUSIVE: 278 case IIOMetadataFormat.VALUE_RANGE_MAX_INCLUSIVE: 279 case IIOMetadataFormat.VALUE_RANGE_MIN_MAX_INCLUSIVE: 280 indent(); 281 println(" <!-- Data type: " + dataTypeNames[dataType] + " -->"); 282 283 boolean minInclusive = 284 (attrValueType & 285 IIOMetadataFormat.VALUE_RANGE_MIN_INCLUSIVE_MASK) != 0; 286 boolean maxInclusive = 287 (attrValueType & 288 IIOMetadataFormat.VALUE_RANGE_MAX_INCLUSIVE_MASK) != 0; 289 indent(); 290 println(" <!-- Min value: " + 291 format.getAttributeMinValue(elementName, attrName) + 292 " " + 293 (minInclusive ? "(inclusive)" : "(exclusive)") + 294 " -->"); 295 String maxValue = 296 format.getAttributeMaxValue(elementName, attrName); 297 // Hack: don't print "infinite" max values 298 if (dataType != IIOMetadataFormat.DATATYPE_INTEGER || 299 !maxValue.equals(maxInteger)) { 300 indent(); 301 println(" <!-- Max value: " + 302 maxValue + 303 " " + 304 (maxInclusive ? "(inclusive)" : "(exclusive)") + 305 " -->"); 306 } 307 break; 308 309 case IIOMetadataFormat.VALUE_LIST: 310 indent(); 311 println(" <!-- Data type: List of " + dataTypeNames[dataType] + " -->"); 312 313 int minLength = 314 format.getAttributeListMinLength(elementName, attrName); 315 if (minLength != 0) { 316 indent(); 317 println(" <!-- Min length: " + 318 minLength + 319 " -->"); 320 } 321 int maxLength = 322 format.getAttributeListMaxLength(elementName, attrName); 323 if (maxLength != Integer.MAX_VALUE) { 324 indent(); 325 println(" <!-- Max length: " + 326 maxLength + 327 " -->"); 328 } 329 break; 330 } 331 } 332 333 private void printObjectInfo(IIOMetadataFormat format, 334 String elementName) { 335 int objectType = format.getObjectValueType(elementName); 336 if (objectType == IIOMetadataFormat.VALUE_NONE) { 337 return; 338 } 339 340 Class objectClass = format.getObjectClass(elementName); 341 if (objectClass != null) { 342 indent(); 343 if (objectType == IIOMetadataFormat.VALUE_LIST) { 344 println(" <!-- User object: array of " + 345 objectClass.getName() + 346 " -->"); 347 } else { 348 println(" <!-- User object: " + 349 objectClass.getName() + 350 " -->"); 351 } 352 353 Object defaultValue = format.getObjectDefaultValue(elementName); 354 if (defaultValue != null) { 355 indent(); 356 println(" <!-- Default value: " + 357 defaultValue.toString() + 358 " -->"); 359 } 360 361 switch (objectType) { 362 case IIOMetadataFormat.VALUE_RANGE: 363 indent(); 364 println(" <!-- Min value: " + 365 format.getObjectMinValue(elementName).toString() + 366 " -->"); 367 indent(); 368 println(" <!-- Max value: " + 369 format.getObjectMaxValue(elementName).toString() + 370 " -->"); 371 break; 372 373 case IIOMetadataFormat.VALUE_ENUMERATION: 374 Object[] enums = format.getObjectEnumerations(elementName); 375 for (int i = 0; i < enums.length; i++) { 376 indent(); 377 println(" <!-- Enumerated value: " + 378 enums[i].toString() + 379 " -->"); 380 } 381 break; 382 383 case IIOMetadataFormat.VALUE_LIST: 384 int minLength = format.getObjectArrayMinLength(elementName); 385 if (minLength != 0) { 386 indent(); 387 println(" <!-- Min length: " + 388 minLength + 389 " -->"); 390 } 391 int maxLength = format.getObjectArrayMaxLength(elementName); 392 if (maxLength != Integer.MAX_VALUE) { 393 indent(); 394 println(" <!-- Max length: " + 395 maxLength + 396 " -->"); 397 } 398 break; 399 } 400 } 401 } 402 403 // Set of elements that have been printed already 404 Set printedElements = new HashSet(); 405 406 // Set of elements that have been scheduled to be printed 407 Set scheduledElements = new HashSet(); 408 409 private void print(IIOMetadataFormat format, 410 String elementName) { 411 // Don't print elements more than once 412 if (printedElements.contains(elementName)) { 413 return; 414 } 415 printedElements.add(elementName); 416 417 // Add the unscheduled children of this node to a list, 418 // and mark them as scheduled 419 List children = new ArrayList(); 420 String[] childNames = format.getChildNames(elementName); 421 if (childNames != null) { 422 for (int i = 0; i < childNames.length; i++) { 423 String childName = childNames[i]; 424 if (!scheduledElements.contains(childName)) { 425 children.add(childName); 426 scheduledElements.add(childName); 427 } 428 } 429 } 430 431 printElementInfo(format, elementName); 432 printObjectInfo(format, elementName); 433 434 ++indentLevel; 435 String[] attrNames = format.getAttributeNames(elementName); 436 for (int i = 0; i < attrNames.length; i++) { 437 printAttributeInfo(format, elementName, attrNames[i]); 438 } 439 440 // Recurse on child nodes 441 Iterator iter = children.iterator(); 442 while (iter.hasNext()) { 443 print(format, (String)iter.next()); 444 } 445 --indentLevel; 446 } 447 448 public static void main(String[] args) { 449 IIOMetadataFormat format = null; 450 if (args.length == 0 || args[0].equals("javax_imageio_1.0")) { 451 format = IIOMetadataFormatImpl.getStandardFormatInstance(); 452 } else { 453 IIORegistry registry = IIORegistry.getDefaultInstance(); 454 Iterator iter = registry.getServiceProviders(ImageReaderSpi.class, 455 false); 456 while (iter.hasNext()) { 457 ImageReaderSpi spi = (ImageReaderSpi)iter.next(); 458 if (args[0].equals 459 (spi.getNativeStreamMetadataFormatName())) { 460 System.out.print(spi.getDescription(null)); 461 System.out.println(": native stream format"); 462 format = spi.getStreamMetadataFormat(args[0]); 463 break; 464 } 465 466 String[] extraStreamFormatNames = 467 spi.getExtraStreamMetadataFormatNames(); 468 if (extraStreamFormatNames != null && 469 Arrays.asList(extraStreamFormatNames). 470 contains(args[0])) { 471 System.out.print(spi.getDescription(null)); 472 System.out.println(": extra stream format"); 473 format = spi.getStreamMetadataFormat(args[0]); 474 break; 475 } 476 477 if (args[0].equals 478 (spi.getNativeImageMetadataFormatName())) { 479 System.out.print(spi.getDescription(null)); 480 System.out.println(": native image format"); 481 format = spi.getImageMetadataFormat(args[0]); 482 break; 483 } 484 485 String[] extraImageFormatNames = 486 spi.getExtraImageMetadataFormatNames(); 487 if (extraImageFormatNames != null && 488 Arrays.asList(extraImageFormatNames).contains(args[0])) { 489 System.out.print(spi.getDescription(null)); 490 System.out.println(": extra image format"); 491 format = spi.getImageMetadataFormat(args[0]); 492 break; 493 } 494 } 495 } 496 497 if (format == null) { 498 System.err.println("Unknown format: " + args[0]); 499 System.exit(0); 500 } 501 502 MetadataFormatPrinter printer = new MetadataFormatPrinter(System.out); 503 printer.print(format); 504 } 505} 506