/* * ImageI/O-Ext - OpenSource Java Image translation Library * http://www.geo-solutions.it/ * http://java.net/projects/imageio-ext/ * (C) 2007 - 2009, GeoSolutions * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * either version 3 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package it.geosolutions.imageio.plugins.jp2k.box; import it.geosolutions.imageio.plugins.jp2k.JP2KBox; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.metadata.IIOInvalidTreeException; import javax.imageio.metadata.IIOMetadataNode; import kdu_jni.Jp2_input_box; import kdu_jni.KduException; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; /** * @author Simone Giannecchini, GeoSolutions * @author Daniele Romagnoli, GeoSolutions */ public class BoxUtilities { private final static Logger LOGGER = Logger.getLogger("BoxUtilities"); public final static String JP2_ASOC_LBL_GML_DATA = "gml.data"; /** * A Hashtable contains the class names for each type of the boxes. This * table will be used to construct a Box object from a Node object by using * reflection. */ public final static Map <Integer, Class<? extends BaseJP2KBox>> boxClasses = new HashMap <Integer, Class<? extends BaseJP2KBox>>(); public final static Map <Integer, String> boxNames = new HashMap <Integer, String>(); /** * The table to link tag names for all the JP2 boxes. */ public final static Map<Integer, String> names = new HashMap<Integer, String>(); public final static Set<String> SUPERBOX_NAMES= new HashSet<String>(); // Initializes the "SUPERBOX_NAMES" set static { SUPERBOX_NAMES.add(JP2HeaderBox.NAME); SUPERBOX_NAMES.add(ResolutionBox.NAME); SUPERBOX_NAMES.add(UUIDInfoBox.NAME); SUPERBOX_NAMES.add(ASOCBox.NAME); SUPERBOX_NAMES.add(CodestreamHeaderBox.NAME); SUPERBOX_NAMES.add(CompositingLayerHeaderBox.NAME); } // Initializes the hash table "names". static { names.put(JP2KFileBox.BOX_TYPE, JP2KFileBox.JP2K_MD_NAME); // children for the root names.put(SignatureBox.BOX_TYPE, SignatureBox.JP2K_MD_NAME); names.put(FileTypeBox.BOX_TYPE, FileTypeBox.JP2K_MD_NAME); // children for the boxes other than // JPEG2000SignatureBox/JPEG2000FileTypeBox names.put(IPRBox.BOX_TYPE, IPRBox.JP2K_MD_NAME); names.put(XMLBox.BOX_TYPE, XMLBox.JP2K_MD_NAME); // Children of HeadCStream names.put(JP2HeaderBox.BOX_TYPE, JP2HeaderBox.JP2K_MD_NAME); names.put(ContiguousCodestreamBox.BOX_TYPE, ContiguousCodestreamBox.JP2K_MD_NAME); // Children of JPEG2000HeaderSuperBox names.put(ImageHeaderBox.BOX_TYPE, ImageHeaderBox.JP2K_MD_NAME); // Optional boxes in JPEG2000HeaderSuperBox names.put(BitsPerComponentBox.BOX_TYPE, BitsPerComponentBox.JP2K_MD_NAME); names.put(ColorSpecificationBox.BOX_TYPE, ColorSpecificationBox.JP2K_MD_NAME); names.put(PaletteBox.BOX_TYPE, PaletteBox.JP2K_MD_NAME); names.put(ComponentMappingBox.BOX_TYPE, ComponentMappingBox.JP2K_MD_NAME); names.put(ChannelDefinitionBox.BOX_TYPE, ChannelDefinitionBox.JP2K_MD_NAME); names.put(ResolutionBox.BOX_TYPE, ResolutionBox.JP2K_MD_NAME); names.put(ASOCBox.BOX_TYPE, ASOCBox.JP2K_MD_NAME); // Children of JPEG2000ResolutionBox names.put(ResolutionBox.BOX_TYPE_CAPTURE, "JPEG2000CaptureResolutionBox"); names.put(ResolutionBox.BOX_TYPE_DEFAULT_DISPLAY, "JPEG2000DefaultDisplayResolutionBox"); // Children of JPEG2000UUIDInfoBox names.put(UUIDBox.BOX_TYPE, UUIDBox.JP2K_MD_NAME); names.put(UUIDInfoBox.BOX_TYPE, UUIDInfoBox.JP2K_MD_NAME); names.put(UUIDListBox.BOX_TYPE, UUIDListBox.JP2K_MD_NAME); names.put(DataEntryURLBox.BOX_TYPE, DataEntryURLBox.JP2K_MD_NAME); // JPX rreq names.put(ReaderRequirementsBox.BOX_TYPE, ReaderRequirementsBox.JP2K_MD_NAME); names.put(CodestreamHeaderBox.BOX_TYPE, CodestreamHeaderBox.JP2K_MD_NAME); names.put(CompositingLayerHeaderBox.BOX_TYPE, CompositingLayerHeaderBox.JP2K_MD_NAME); // JPX Label Box names.put(LabelBox.BOX_TYPE, LabelBox.JP2K_MD_NAME); } // Initializes the hash table "boxClasses". static { // children for the root boxClasses.put(JP2KFileBox.BOX_TYPE, JP2KFileBox.class); boxClasses.put(SignatureBox.BOX_TYPE, SignatureBox.class); boxClasses.put(FileTypeBox.BOX_TYPE, FileTypeBox.class); // children for the boxes other than // JPEG2000SignatureBox/JPEG2000FileTypeBox boxClasses.put(IPRBox.BOX_TYPE, IPRBox.class); boxClasses.put(XMLBox.BOX_TYPE, XMLBox.class); boxClasses.put(JP2HeaderBox.BOX_TYPE, JP2HeaderBox.class); boxClasses.put(ContiguousCodestreamBox.BOX_TYPE, ContiguousCodestreamBox.class); // Children of JPEG2000HeaderSuperBox boxClasses.put(ImageHeaderBox.BOX_TYPE, ImageHeaderBox.class); // Optional boxes in JPEG2000HeaderSuperBox boxClasses.put(BitsPerComponentBox.BOX_TYPE, BitsPerComponentBox.class); boxClasses.put(ColorSpecificationBox.BOX_TYPE, ColorSpecificationBox.class); boxClasses.put(PaletteBox.BOX_TYPE, PaletteBox.class); boxClasses.put(ComponentMappingBox.BOX_TYPE, ComponentMappingBox.class); boxClasses.put(ChannelDefinitionBox.BOX_TYPE, ChannelDefinitionBox.class); boxClasses.put(ResolutionBox.BOX_TYPE, ResolutionBox.class); boxClasses.put(ASOCBox.BOX_TYPE, ASOCBox.class); // Children of JPEG2000ResolutionBox boxClasses.put(ResolutionBox.BOX_TYPE_CAPTURE, CaptureResolutionBox.class); boxClasses.put(ResolutionBox.BOX_TYPE_DEFAULT_DISPLAY, DefaultDisplayResolutionBox.class); // Children of JPEG2000UUIDInfoBox boxClasses.put(UUIDInfoBox.BOX_TYPE, UUIDInfoBox.class); boxClasses.put(UUIDBox.BOX_TYPE, UUIDBox.class); boxClasses.put(UUIDListBox.BOX_TYPE, UUIDListBox.class); boxClasses.put(DataEntryURLBox.BOX_TYPE, DataEntryURLBox.class); // JPX rreq boxClasses.put(ReaderRequirementsBox.BOX_TYPE, ReaderRequirementsBox.class); boxClasses.put(CodestreamHeaderBox.BOX_TYPE, CodestreamHeaderBox.class); boxClasses.put(CompositingLayerHeaderBox.BOX_TYPE, CompositingLayerHeaderBox.class); // JPX Label Box boxClasses.put(LabelBox.BOX_TYPE, LabelBox.class); } static { // children for the root boxNames.put(JP2KFileBox.BOX_TYPE, JP2KFileBox.NAME); boxNames.put(SignatureBox.BOX_TYPE, SignatureBox.NAME); boxNames.put(FileTypeBox.BOX_TYPE, FileTypeBox.NAME); // children for the boxes other than // JPEG2000SignatureBox/JPEG2000FileTypeBox boxNames.put(IPRBox.BOX_TYPE, IPRBox.NAME); boxNames.put(XMLBox.BOX_TYPE, XMLBox.NAME); // Children of HeadCStream boxNames.put(JP2HeaderBox.BOX_TYPE, JP2HeaderBox.NAME); boxNames.put(ContiguousCodestreamBox.BOX_TYPE, ContiguousCodestreamBox.NAME); boxNames.put(ASOCBox.BOX_TYPE, ASOCBox.NAME); // Children of JPEG2000HeaderSuperBox boxNames.put(ImageHeaderBox.BOX_TYPE, ImageHeaderBox.NAME); // Optional boxes in JPEG2000HeaderSuperBox boxNames.put(BitsPerComponentBox.BOX_TYPE, BitsPerComponentBox.NAME); boxNames.put(ColorSpecificationBox.BOX_TYPE, ColorSpecificationBox.NAME); boxNames.put(PaletteBox.BOX_TYPE, PaletteBox.NAME); boxNames.put(ComponentMappingBox.BOX_TYPE, ComponentMappingBox.NAME); boxNames.put(ChannelDefinitionBox.BOX_TYPE, ChannelDefinitionBox.NAME); boxNames.put(ResolutionBox.BOX_TYPE, ResolutionBox.NAME); // Children of JPEG2000ResolutionBox boxNames.put(ResolutionBox.BOX_TYPE_CAPTURE, CaptureResolutionBox.CAP_NAME); boxNames.put(ResolutionBox.BOX_TYPE_DEFAULT_DISPLAY, DefaultDisplayResolutionBox.DEF_NAME); boxNames.put(UUIDBox.BOX_TYPE, UUIDBox.NAME); boxNames.put(UUIDInfoBox.BOX_TYPE, UUIDInfoBox.NAME); // Children of JPEG2000UUIDInfoBox boxNames.put(UUIDListBox.BOX_TYPE, UUIDListBox.NAME); boxNames.put(DataEntryURLBox.BOX_TYPE, DataEntryURLBox.NAME); // JPX rreq boxNames.put(ReaderRequirementsBox.BOX_TYPE, ReaderRequirementsBox.NAME); boxNames.put(CodestreamHeaderBox.BOX_TYPE, CodestreamHeaderBox.NAME); boxNames.put(CompositingLayerHeaderBox.BOX_TYPE, CompositingLayerHeaderBox.NAME); // JPX Label Box boxNames.put(LabelBox.BOX_TYPE, LabelBox.NAME); } /** * Copies that four bytes of an integer into the byte array. Necessary for * the subclasses to compose the content array from the data elements */ public static void copyInt(final byte[] data, int pos, final int value) { data[pos++] = (byte) ((value >> 24)& 0xFF); data[pos++] = (byte) ((value >> 16)& 0xFF); data[pos++] = (byte) ((value >> 8)& 0xFF); data[pos++] = (byte) (value ); } /** * Creates a <code>Box</code> object with the provided <code>type</code> * based on the provided Node object based on reflection. * * @todo handle Exception */ public static JP2KBox createBox(int type, Node node) throws IIOInvalidTreeException { Class<? extends JP2KBox> boxClass =boxClasses.get(new Integer(type)); try { // gets the constructor with <code>Node</code> parameter Constructor<? extends JP2KBox> cons = boxClass.getConstructor(new Class[] { Node.class }); if (cons != null) { return cons.newInstance(new Object[] { node }); } } catch (NoSuchMethodException e) { LOGGER.log(Level.SEVERE,e.getLocalizedMessage(),e); } catch (InvocationTargetException e) { LOGGER.log(Level.SEVERE,e.getLocalizedMessage(),e); } catch (IllegalAccessException e) { LOGGER.log(Level.SEVERE,e.getLocalizedMessage(),e); } catch (InstantiationException e) { LOGGER.log(Level.SEVERE,e.getLocalizedMessage(),e); } throw new IllegalArgumentException("The provided type or Node are not valid"); } /** * Creates a <code>Box</code> object with the provided <code>type</code> * based on the provided data object based on reflection. */ public static JP2KBox createBox(int type, byte[] data) { Class<? extends JP2KBox> boxClass = boxClasses.get(new Integer(type)); try { //super box elements have default contructors if(data==null) return boxClass.newInstance(); // gets the constructor with <code>byte[]</code> parameter final Constructor<? extends JP2KBox> cons = boxClass.getConstructor(Array.newInstance( byte.class, 0).getClass()); if (cons != null) { return cons.newInstance(new Object[] { data }); } } catch (NoSuchMethodException e) { LOGGER.log(Level.SEVERE,e.getLocalizedMessage(),e); } catch (InvocationTargetException e) { LOGGER.log(Level.SEVERE,e.getLocalizedMessage(),e); } catch (IllegalAccessException e) { LOGGER.log(Level.SEVERE,e.getLocalizedMessage(),e); } catch (InstantiationException e) { LOGGER.log(Level.SEVERE,e.getLocalizedMessage(),e); } throw new IllegalArgumentException("The provided type or data are not valid"); } /** Extracts the value of the attribute from name. */ public static Object getAttribute(Node node, String name) { NamedNodeMap map = node.getAttributes(); node = map.getNamedItem(name); return (node != null) ? node.getNodeValue() : null; } /** Gets the byte array from an <code>IIOMetadataNode</code>. */ public static byte[] getByteArrayElementValue(Node node) { if (node instanceof IIOMetadataNode) { Object obj = ((IIOMetadataNode) node).getUserObject(); if (obj instanceof byte[]) return (byte[]) obj; } return parseByteArray(node.getNodeValue()); } /** Gets its byte value from an <code>IIOMetadataNode</code>. */ public static byte getByteElementValue(Node node) { if (node instanceof IIOMetadataNode) { Object obj = ((IIOMetadataNode) node).getUserObject(); if (obj instanceof Byte) return ((Byte) obj).byteValue(); } String value = node.getNodeValue(); if (value != null) return new Byte(value).byteValue(); return (byte) 0; } /** Gets the integer array from an <code>IIOMetadataNode</code>. */ public static int[] getIntArrayElementValue(Node node) { if (node instanceof IIOMetadataNode) { Object obj = ((IIOMetadataNode) node).getUserObject(); if (obj instanceof int[]) return (int[]) obj; } return parseIntArray(node.getNodeValue()); } /** Gets its integer value from an <code>IIOMetadataNode</code>. */ public static int getIntElementValue(Node node) { if (node instanceof IIOMetadataNode) { Object obj = ((IIOMetadataNode) node).getUserObject(); if (obj instanceof Integer) return ((Integer) obj).intValue(); } String value = node.getNodeValue(); if (value != null) return new Integer(value).intValue(); return 0; } /** * Returns the XML tag name defined in JP2 XML xsd/dtd for the box with the * provided <code>type</code>. If the <code>type</code> is not known, * the string <code>"unknown"</code> is returned. */ public static String getName(int type) { String name = names.get(new Integer(type)); return name == null ? "unknown" : name; } /** * Returns the BoxName for the box with the provided <code>type</code>. * If the <code>type</code> is not known, the string * <code>"unknown"</code> is returned. */ public static String getBoxName(int type) { String name = boxNames.get(new Integer(type)); return name == null ? "unknown" : name; } /** Gets its short value from an <code>IIOMetadataNode</code>. */ public static short getShortElementValue(Node node) { if (node instanceof IIOMetadataNode) { Object obj = ((IIOMetadataNode) node).getUserObject(); if (obj instanceof Short) return ((Short) obj).shortValue(); } String value = node.getNodeValue(); if (value != null) return new Short(value).shortValue(); return (short) 0; } /** * Gets its <code>String</code> value from an <code>IIOMetadataNode</code>. */ public static String getStringElementValue(Node node) { if (node instanceof IIOMetadataNode) { Object obj = ((IIOMetadataNode) node).getUserObject(); if (obj instanceof String) return (String) obj; } return node.getNodeValue(); } /** Returns the type String based on the provided name. */ public static String getTypeByName(String name) { for (Map.Entry<Integer,String> entry:names.entrySet()) { if (name.equals(entry.getValue())) return getTypeString(entry.getKey()); } return null; } /** * Converts the box type from integer to string. This is necessary because * type is defined as String in xsd/dtd and integer in the box classes. */ public static int getTypeInt(String s) { byte[] buf = s.getBytes(); int t = buf[0]; for (int i = 1; i < 4; i++) { t = (t << 8) | buf[i]; } return t; } /** * Converts the box type from integer to string. This is necessary because * type is defined as String in xsd/dtd and integer in the box classes. */ public static String getTypeString(int type) { byte[] buf = new byte[4]; for (int i = 3; i >= 0; i--) { buf[i] = (byte) (type & 0xFF); type >>>= 8; } return new String(buf); } /** Parses the byte array expressed by a string. */ public static byte[] parseByteArray(String value) { if (value == null) return null; StringTokenizer token = new StringTokenizer(value); int count = token.countTokens(); byte[] buf = new byte[count]; int i = 0; while (token.hasMoreElements()) { buf[i++] = new Byte(token.nextToken()).byteValue(); } return buf; } /** Parses the integer array expressed a string. */ public static int[] parseIntArray(String value) { if (value == null) return null; StringTokenizer token = new StringTokenizer(value); int count = token.countTokens(); int[] buf = new int[count]; int i = 0; while (token.hasMoreElements()) { buf[i++] = new Integer(token.nextToken()).intValue(); } return buf; } /** * Returns the Box class for the box with the provided <code>type</code>. */ public static Class<? extends BaseJP2KBox> getBoxClass(int type) { switch (type) { case ResolutionBox.BOX_TYPE: case ResolutionBox.BOX_TYPE_CAPTURE: case ResolutionBox.BOX_TYPE_DEFAULT_DISPLAY: return ResolutionBox.class; } return boxClasses.get(type); } /** * Return the numeric decimal value of an ASCII code representing a * Hexadecimal value. * * @param c * the ASCII code representing a Hexadecimal value. * @return the numeric decimal value of an ASCII code representing a * Hexadecimal value. */ public static int getValue(int c) { if (c < 58 && c >= 48) return c - 48; else if (c < 71 && c >= 65) return c - 55; return -1; } /** * * @param box * @return * @throws KduException * TODO optimize me */ public static byte[] getContent(final Jp2_input_box box) throws KduException { final int nBytes = (int) box.Get_box_bytes(); final byte[] buffer = new byte[nBytes]; int readBytes = box.Read(buffer, nBytes); final byte[] destBuffer = new byte[readBytes]; System.arraycopy(buffer, 0, destBuffer, 0, readBytes); return destBuffer; } }