/* * ImageI/O-Ext - OpenSource Java Image translation Library * http://www.geo-solutions.it/ * http://java.net/projects/imageio-ext/ * (C) 2008, 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; import it.geosolutions.imageio.plugins.jp2k.box.BoxUtilities; import it.geosolutions.imageio.plugins.jp2k.box.JP2KFileBox; import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; import kdu_jni.Jp2_family_src; import kdu_jni.Jp2_input_box; import kdu_jni.Jp2_locator; import kdu_jni.KduException; /** * Parses a JP2K File into a TreeModel of boxes. * * <p> * Note that for the moment we are doing basic parsing which means that we try * to recognize as many boxes as possible but we uses the {@link Jp2_family_src} * object from kakadu. In the future we should allow this file walker to walk * through fragmented codestream as in general JPX files. * * @author Simone Giannecchini, GeoSolutions * */ class JP2KFileWalker { /** {@link Logger} for this {@link JP2KFileWalker}. */ private final static Logger LOGGER = Logger.getLogger("JP2KFileWalker"); /** * Tells me whether or not this {@link JP2KFileWalker} has been initialized * or not. */ private boolean initialized; /** * Contains all the boxes this {@link JP2KFileWalker} has been able to * recognize, parsed in a tree model. */ private DefaultTreeModel tree; /** * File name of the underlying jpeg2k source. */ private String fileName; /** * Object used to parse the underlying file. */ private Jp2_family_src familySource; /** * */ public JP2KFileWalker(final String fileName) { this.fileName = fileName; } /** * Parses this file into a {@link TreeModel} object. */ private void init() { if (this.initialized) return; // // // // create needed kakadu machinery // // // familySource = new Jp2_family_src(); final Jp2_input_box inputBox = new Jp2_input_box(); final Jp2_locator locator = new Jp2_locator(); List<? extends Throwable> exceptions = Collections.emptyList(); try { // open the family familySource.Open(fileName); // create the needed objects final JP2KFileBox box = new JP2KFileBox(); this.tree = new DefaultTreeModel(box); // Add a listener: a new instance of a JP2KTreeController on the tree final JP2KTreeController controller = new JP2KTreeController(this.tree); this.tree.addTreeModelListener(controller); // recursive parsing if (inputBox.Open(familySource, locator)) { parse(inputBox, box, 0); } controller.checkTreeConsistency(); // clean up inputBox.Close(); } catch (KduException e) { throw new RuntimeException( "Error caused by a Kakadu exception during Box management! ", e); } finally { // clean up try { familySource.Close(); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.log(Level.FINEST, e.getLocalizedMessage(), e); } try { if (inputBox != null) inputBox.Close(); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.log(Level.FINEST, e.getLocalizedMessage(), e); } } if (!exceptions.isEmpty()){ // TODO Create a specific exception that wraps them all! throw new IllegalStateException("Tree Check failed"); } } /** * Parses this box, his brothers as well as his children recursively. * * @param inputBox * the box to begin the parsing with. * @param parent * the parent box to which attach this box as well as its * brothers. * @param index * the index at which this box should be attached in his * father's children list. * @throws KduException * in case any problem with the low level machinery happens. */ private void parse(final Jp2_input_box inputBox, final JP2KBox parent, int index) throws KduException { final Jp2_input_box childBox = new Jp2_input_box(); try { // // // // get the info for this box // // // final int boxtype = (int) (0xffffffff & inputBox.Get_box_type()); final String typeString = BoxUtilities.getTypeString(boxtype); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Found box " + typeString); } LazyJP2KBox currentBox = null; // // // // get the content of the box // // // if (BoxUtilities.boxNames.containsKey(boxtype)) { final Jp2_locator locator = inputBox.Get_locator(); // created a lazily loaded box currentBox = new LazyJP2KBox(this.fileName, boxtype, locator); parent.insert(currentBox, index++); this.tree.nodesWereInserted(parent, new int[] { index - 1 }); } else { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.info("Box of type " + typeString + " cannot be handled by this file type reader"); } } // // // // check children // // // // reopen box if (BoxUtilities.SUPERBOX_NAMES.contains(typeString)) { if (childBox.Open(inputBox)) { if (childBox.Exists()) { parse(childBox, currentBox, 0); } // close box childBox.Close(); } } // // // // check next // // // // close box inputBox.Close(); if (inputBox.Open_next()) { if (inputBox.Exists()) { parse(inputBox, parent, index); } } } catch (KduException e) { throw new RuntimeException( "Error caused by a Kakadu exception during Box management! ", e); } finally { // clean up try { if (childBox != null) childBox.Close(); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.log(Level.FINEST, e.getLocalizedMessage(), e); } try { if (inputBox != null) inputBox.Close(); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINEST)) LOGGER.log(Level.FINEST, e.getLocalizedMessage(), e); } } } /** * Retrieves a cached {@link TreeModel} instance which represent the * underlying jpeg2k file. * * @return a cached {@link TreeModel} instance which represent the * underlying jpeg2k file. */ public synchronized TreeModel getJP2KBoxesTree() { init(); return this.tree; } }