net.algart.simagis.imageio.IIOMetadataToJsonConverter.java Source code

Java tutorial

Introduction

Here is the source code for net.algart.simagis.imageio.IIOMetadataToJsonConverter.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2014-2016 Daniel Alievsky, AlgART Laboratory (http://algart.net)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package net.algart.simagis.imageio;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

public class IIOMetadataToJsonConverter {
    private static final String XMP_CODE_LINE_IN_JPEG = "http://ns.adobe.com/xap/1.0/\u0000";
    private static final int MAX_NON_COMPACTED_SIMILAR_CHILDREN = 4;
    private static final String[] COMPACTED_SIMILAR_CHILDREN_NAMES = { "TIFFByte", "TIFFShort", "TIFFLong" };

    // Maybe it will have some customized parameters in future

    public IIOMetadataToJsonConverter() {
    }

    public final JSONObject toJson(IIOMetadata metadata) throws JSONException {
        if (metadata == null)
            throw new NullPointerException("Null metadata");
        JSONObject result = new JSONObject();
        JSONArray formats = new JSONArray();
        final String[] formatNames = metadata.getMetadataFormatNames();
        if (formatNames != null) {
            for (String formatName : formatNames) {
                final Node tree = metadata.getAsTree(formatName);
                JSONObject format = new JSONObject();
                format.put("formatName", formatName);
                format.put("tree", treeToJson(tree));
                formats.put(format);
            }
        }
        result.put("formats", formats);
        return result;
    }

    private JSONObject treeToJson(Node node) throws JSONException {
        JSONObject result = new JSONObject();
        result.put("name", node.getNodeName());
        result.put("value", node.getNodeValue());
        final NamedNodeMap nodeAttributes = node.getAttributes();
        if (nodeAttributes != null) {
            final int length = nodeAttributes.getLength();
            if (length > 0) {
                JSONObject attributes = new JSONObject();
                for (int k = 0; k < length; k++) {
                    final Node nodeAttr = nodeAttributes.item(k);
                    attributes.put(nodeAttr.getNodeName(), nodeAttr.getNodeValue());
                }
                result.put("attributes", attributes);
            }
        }
        JSONObject compactChildren = null;
        for (String childrenName : COMPACTED_SIMILAR_CHILDREN_NAMES) {
            compactChildren = compactTIFFSimilarNodes(node, childrenName);
            if (compactChildren != null) {
                break;
            }
        }
        if (compactChildren != null) {
            result.put("joinedNodes", compactChildren);
        } else {
            Node child = node.getFirstChild();
            if (child != null) {
                JSONArray nodes = new JSONArray();
                do {
                    nodes.put(treeToJson(child));
                    child = child.getNextSibling();
                } while (child != null);
                result.put("nodes", nodes);
            }
        }
        if (node instanceof IIOMetadataNode) {
            result.put("userObject", iioNodeUserObjectToJson((IIOMetadataNode) node));
        }
        return result;
    }

    private JSONObject compactTIFFSimilarNodes(Node node, String childrenName) throws JSONException {
        assert node != null;
        assert childrenName != null;
        long count = 0;
        for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling(), count++) {
            if (!childrenName.equals(child.getNodeName())) {
                return null;
            }
            // so, a child has the name, equal to childrenName
            if (child.getFirstChild() != null) {
                return null;
            }
            // so, a child has no own children
            final NamedNodeMap childAttributes = child.getAttributes();
            if (childAttributes == null || childAttributes.getLength() != 1) {
                return null;
            }
            Node childAttr = childAttributes.item(0);
            String attrName = childAttr.getNodeName();
            String attrValue = childAttr.getNodeValue();
            if (!("value".equals(attrName)) || attrValue == null || attrValue.contains(",")) {
                return null;
            }
            // so, a child has only 1 attribute "value", which is a string, not containing ","
        }
        if (count <= MAX_NON_COMPACTED_SIMILAR_CHILDREN) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        count = 0;
        for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling(), count++) {
            Node childAttr = child.getAttributes().item(0);
            if (count > 0) {
                sb.append(",");
            }
            sb.append(childAttr.getNodeValue());
        }
        JSONObject result = new JSONObject();
        result.put("childrenName", childrenName);
        result.put("childrenValue", sb.toString());
        return result;
    }

    private JSONObject iioNodeUserObjectToJson(IIOMetadataNode node) throws JSONException {
        final Object userObject = node.getUserObject();
        if (userObject == null) {
            return null;
        }
        JSONObject result = new JSONObject();
        result.put("class", userObject.getClass().getCanonicalName());
        final String stringRepresentation = userObject.toString();
        final String javaDefaultStringRepresentation = userObject.getClass().getName() + "@"
                + Integer.toHexString(System.identityHashCode(userObject)); // see JavaDoc to Object.toString
        if (!stringRepresentation.equals(javaDefaultStringRepresentation)) {
            // so, we can hope for some non-trivial information in toString()
            result.put("toString", stringRepresentation);
        }
        if (userObject instanceof byte[]) {
            byte[] bytes = (byte[]) userObject;
            result.put("valueLength", bytes.length);
            if (bytes.length <= 16384) {
                StringBuilder sb = new StringBuilder();
                for (int k = 0; k < bytes.length; k++) {
                    // formatting bytes like it is done in TIFFUndefined value in TIFF metadata
                    if (k > 0) {
                        sb.append(",");
                    }
                    sb.append(bytes[k] & 0xFF);
                }
                result.put("valueBytes", sb.toString());
            }
        }
        JSONObject extendedInfo;
        try {
            extendedInfo = extendedUserObjectToJson(userObject);
        } catch (Exception e) {
            extendedInfo = exceptionToJson(e);
        }
        result.put("extendedInfo", extendedInfo);
        return result;
    }

    private JSONObject extendedUserObjectToJson(Object userObject) throws JSONException, IOException {
        if (userObject instanceof byte[]) {
            byte[] bytes = (byte[]) userObject;
            if (bytes[0] == 'E' && bytes[1] == 'x' && bytes[2] == 'i' && bytes[3] == 'f' && bytes[4] == 0
                    && bytes[5] == 0) {
                final InputStream inputStream = new ByteArrayInputStream(bytes, 6, bytes.length - 6);
                try {
                    final JSONObject exif = extendedReadExif(inputStream);
                    if (exif == null) {
                        return null;
                    }
                    JSONObject result = new JSONObject();
                    result.put("com.simagis.imageio.metadata.Exif", exif);
                    //                    System.err.println("Found com.simagis.imageio.metadata.Exif!");
                    return result;
                } finally {
                    inputStream.close();
                }
            }
            final int xmpCodeLineLength = XMP_CODE_LINE_IN_JPEG.length();
            boolean isXMP = bytes.length >= xmpCodeLineLength;
            if (isXMP) {
                for (int k = 0; k < xmpCodeLineLength; k++) {
                    isXMP &= bytes[k] == XMP_CODE_LINE_IN_JPEG.charAt(k);
                }
            }
            if (isXMP) {
                String s = new String(bytes, xmpCodeLineLength, bytes.length - xmpCodeLineLength, "UTF-8");
                JSONObject result = new JSONObject();
                result.put("com.simagis.imageio.metadata.XMP", s);
                //                System.err.println("Found com.simagis.imageio.metadata.XMP!");
                return result;
            }
        }
        return null;
    }

    private JSONObject extendedReadExif(InputStream inputStream) throws IOException, JSONException {
        assert inputStream != null;
        ImageInputStream iis = ImageIO.createImageInputStream(inputStream);
        Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
        try {
            if (!readers.hasNext()) {
                return null; // no readers to read such "Exif"
            }
            ImageReader reader = readers.next();
            try {
                reader.setInput(iis);
                return toJson(reader.getImageMetadata(0));
            } finally {
                reader.dispose();
            }
        } finally {
            iis.close();
        }
    }

    private static JSONObject exceptionToJson(Exception e) throws JSONException {
        JSONObject result = new JSONObject();
        result.put("error", e.toString());
        return result;
    }
}