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